/**
  *  @function Timsort is a hybrid stable sorting algorithm, derived from merge sort and insertion sort,
  *  designed to perform well on many kinds of real-world data.
  *  It was implemented by Tim Peters in 2002 for use in the Python programming language.
  *  It is also used to sort arrays of non-primitive type in Java SE 7,
  *  on the Android platform, in GNU Octave, on V8, Swift and Rust.
  *  1) It sorts small partitions using Insertion Sort.
  *  2) Merges the partition using Merge Sort.
  *  @see [Timsort](https://en.wikipedia.org/wiki/Timsort)
  *  @param {Array} array
  */

const Timsort = (array) => {
  // Default size of a partition
  const RUN = 32
  const n = array.length
  // Sorting the partitions using Insertion Sort
  for (let i = 0; i < n; i += RUN) {
    InsertionSort(array, i, Math.min(i + RUN - 1, n - 1))
  }
  for (let size = RUN; size < n; size *= 2) {
    for (let left = 0; left < n; left += 2 * size) {
      const mid = left + size - 1
      const right = Math.min(left + 2 * size - 1, n - 1)
      Merge(array, left, mid, right)
    }
  }
}

/**
 * @function performs insertion sort on the partition
 * @param {Array} array array to be sorted
 * @param {Number} left left index of partiton
 * @param {Number} right right index of partition
 */

const InsertionSort = (array, left, right) => {
  for (let i = left + 1; i <= right; i++) {
    const key = array[i]
    let j = i - 1
    while (j >= left && array[j] > key) {
      array[j + 1] = array[j]
      j--
    }
    array[j + 1] = key
  }
}

/**
 * @function merges two sorted partitions
 * @param {Array} array array to be sorted
 * @param {Number} left left index of partition
 * @param {Number} mid mid index of partition
 * @param {Number} right right index of partition
 */

const Merge = (array, left, mid, right) => {
  if (mid >= right) return
  const len1 = mid - left + 1
  const len2 = right - mid
  const larr = Array(len1)
  const rarr = Array(len2)
  for (let i = 0; i < len1; i++) {
    larr[i] = array[left + i]
  }
  for (let i = 0; i < len2; i++) {
    rarr[i] = array[mid + 1 + i]
  }
  let i = 0; let j = 0; let k = left
  while (i < larr.length && j < rarr.length) {
    if (larr[i] < rarr[j]) {
      array[k++] = larr[i++]
    } else {
      array[k++] = rarr[j++]
    }
  }
  while (i < larr.length) {
    array[k++] = larr[i++]
  }
  while (j < rarr.length) {
    array[k++] = rarr[j++]
  }
}

/**
 * @example Test of Timsort functions.
 * Data is randomly generated.
 * Prints "RIGHT" if it works as expected,
 * otherwise "FAULTY"
 */
(() => {
  const size = 1000000
  const data = Array(size)
  for (let i = 0; i < size; i++) {
    data[i] = Math.random() * Number.MAX_SAFE_INTEGER
  }
  const isSorted = function (array) {
    const n = array.length
    for (let i = 0; i < n - 1; i++) {
      if (array[i] > array[i + 1]) return false
    }
    return true
  }
  Timsort(data)
  if (isSorted(data)) {
    console.log('RIGHT')
  } else {
    console.log('FAULTY')
  }
})()
